$(function()
{
/**
 * グローバル変数
 */
var g_scale = 1;
var g_color = "";
var g_idea_map = {};
var g_selected_id = new Array();


/**
 * 現在のスケールを取得する。
 * @return スケール(0～1) 
 */
function getGlobalScale()
{
	return g_scale;
}

/**
 * クライアントカラーを取得する。
 * @return 色
 */
function getGlobalColor()
{
	if (g_color == "")
	{
		// クライアントの色を決める（ランダム）
		var color_rgb = [];
		color_rgb[0] = Math.floor(Math.random() * 127); 
		color_rgb[1] = Math.floor(Math.random() * 127); 
		color_rgb[2] = Math.floor(Math.random() * 127);
		 
		// パステルカラーにする
		color_rgb[Math.floor(Math.random() * 3)] = 255;
		
		// RGBカラーの結合
		g_color = color_rgb[0] + "," + color_rgb[1] + "," + color_rgb[2];
	}
	return g_color;
}

/**
 * 初期描画を行う。
 */
function initDraw()
{
	for (var i in g_idea_map)
	{
		newDraw(g_idea_map[i]);
	}
}

/**
 * 新規アイデアの描画を行う。
 * @param 描画対象のアイデアオブジェクト
 */
function newDraw(targetIdea)
{
	$("body").append(targetIdea.toTag());
}

/**
 * アイデア一族の再描画を行う。
 * @param 描画対象のアイデアオブジェクト 
 */
function refreshDraw(targetIdea)
{
	// 祖先を検索する
	var tmpIdea = targetIdea;
	while (tmpIdea.getParentId() != "")
	{
		tmpIdea = g_idea_map[String(tmpIdea.getParentId())];
	}
	
	tmpIdea.invalidate();
} 

/**
 * アイデアクラス
 */
var Idea = function(id, comment, color, size, parent_id)
{
	// ID
	this.id = id;
	// 親ID
	this.parent_id = (parent_id)? parent_id : "";
	// コメント
	this.comment = comment;
	// 子のIDリスト
	this.children = new Array();
	// 背景色
	this.color = color;
	// サイズ
	this.size = size;
	// 位置(y)
	this.top = Math.floor(Math.random() * ($("html").height() - size));
	// 位置(x)
	this.left = Math.floor(Math.random() * ($("body").width() - size));
	
	
	/**
	 * 子を追加する。
	 * @param id 子のID 
	 */
	this.addChild = function(id)
	{
		this.children.push(id);
	}
	
	/**
	 * 親IDを取得する。
	 * @return 親ID 
	 */
	this.getParentId = function()
	{
		return this.parent_id;
	}
	
	/**
	 * 親子関係を持たないアイデアであるかどうかを取得する。
	 */
	this.isAlone = function()
	{
		if (this.parent_id == "" && this.children.length == 0)
		{
			return true;
		}
		return false;
	}
	
	/**
	 * 直径を計算する。
	 * @return 直径 
	 */
	this.calcSize = function()
	{
		return Math.floor( (Math.floor(this.children.length / 3) * 15 + 100) * getGlobalScale());
	}
	
	/**
	 * 文字サイズを計算する。
	 * @return 文字サイズ 
	 */
	this.calcFontSize = function()
	{
		return Math.floor(16 * getGlobalScale());
	}
	
	/**
	 * 選択された場合の動作を規定する。
	 */
	this.select = function()
	{
		// 選択状態を規定したCSSを適用
		$("#" + this.id).css("-webkit-animation-name", "fuyopika_motion");
	}
	
	/**
	 * 選択解除された場合の動作を規定する。
	 */
	this.deselect = function()
	{
		// 選択解除状態を規定したCSSを適用
		$("#" + this.id).css("-webkit-animation-name", "fuyo_motion");
	}
	
	/**
	 * 再描画を行う。
	 */
	this.invalidate = function()
	{
		// アイデアを取得
		var idea = $("#" + this.id);
		// サイズを取得
		var parentSize = this.calcSize();
		// 文字サイズを取得
		var tmp_font_size = this.calcFontSize();
		// 子アイデアの描画角度を算出
		var deg = 360 / this.children.length;
		
		// 子アイデアの表示位置を更新
		for (var i = 0; i<this.children.length; i++)
		{
			var childSize = g_idea_map[String(this.children[i])].calcSize();
			
			// 子の位置を計算
			var distant = Math.floor((parentSize + childSize) / 2);
			var diff = Math.floor((parentSize - childSize) / 2);
			var x = Math.floor(this.left + diff + distant * Math.cos(deg * i * Math.PI / 180));
			var y = Math.floor(this.top + diff + distant * Math.sin(deg * i * Math.PI / 180));
			
			g_idea_map[String(this.children[i])].top = y;
			g_idea_map[String(this.children[i])].left = x;
		}
		
		// 自分の表示位置を更新
		idea.parent().css({
			"top" : this.top + "px",
			"left" : this.left + "px"
		});
		idea.css({
			"font-size" : tmp_font_size + "px",
			"width" : parentSize + "px",
			"height" : parentSize + "px",
			"border-radius" : Math.floor(parentSize)/2 + "px",
			"-moz-border-radius" : Math.floor(parentSize)/2 + "px",
			"-webkit-border-radius" : Math.floor(parentSize)/2 + "px",
			"background-image" : "-webkit-gradient(radial, center center, 0, center center, " + parentSize * 0.75 + ", from(rgba(" + this.color + ", 1)), to(rgba(255, 255, 255, 1)))",
		});
		
		// 子要素の再描画を行う
		for (var i = 0; i < this.children.length; i++)
		{
			g_idea_map[String(this.children[i])].invalidate();
		}
	}
	
	/**
	 * タグオブジェクトとして取得する。
	 */
	this.toTag = function()
	{
		// サイズ情報を取得
		var tmp_size = this.calcSize();
		var tmp_font_size = this.calcFontSize();
		
		// アイデアのコンテナを生成
		var container = $("<div>")
			.addClass("idea_container")
			.css({
				"top" : this.top + "px",
				"left" : this.left + "px"
			});
		
		// アイデアそのものを生成
		var idea = $("<div>")
			.attr("id", this.id)
			.addClass("idea")
			.text(this.comment)
			.css({
				"font-size" : tmp_font_size + "px",
				"width" : tmp_size + "px",
				"height" : tmp_size + "px",
				"border-radius" : Math.floor(tmp_size)/2 + "px",
				"-moz-border-radius" : Math.floor(tmp_size)/2 + "px",
				"-webkit-border-radius" : Math.floor(tmp_size)/2 + "px",
				"background" : "-moz-radial-gradient( cover, rgba(" + this.color + ",1), rgba(255, 255, 255, 1))",
				"background-image" : "-webkit-gradient(radial, center center, 0, center center, " + tmp_size * 0.75 + ", from(rgba(" + this.color + ", 1)), to(rgba(255, 255, 255, 1)))",
				"border" : "1px solid rgba(" + this.color + ", 0.7)"
			})
			.appendTo(container);
		
		return container;
	}
}
	
/**
 * サーバーからの通知を表示する。
 * @param obj サーバーからのメッセージオブジェクト
 */
function message(obj)
{
	if ("announcement" in obj)
	{
		// チャット表示領域の更新
		$("<p>").append($("<em>").text(esc(obj.announcement))).appendTo($("div#chat"));
	}
	else if ("message" in obj)
	{
		// チャット表示領域の更新
		$("<p>").append($("<b>").text(esc(obj.message[0]) + ":")).append(esc(obj.message[2])).appendTo($("div#chat"));
		
		// メッセージの情報を取得
		var id = obj.message[1];
		var txt = obj.message[2];
		var color = obj.message[3];
		var parentId = obj.message[4];
		var size = 100; // サーバーからわたってこないので固定値
		
		// アイデア生成
		var newIdea = new Idea(id, txt, color, size, parentId);
		// マップへ追加
		g_idea_map[String(id)] = newIdea;
		
		if (parentId && g_idea_map[String(parentId)]) // TODO 「&&」の後は暫定コードである、サーバーから全データが送信されるべき
		{
			// 親へ子の追加を通知
			g_idea_map[String(parentId)].addChild(id);
		}
		
		// 新アイデアを描画
		newDraw(newIdea);
		
		if (parentId)
		{
			// 系列アイデアを再描画
			refreshDraw(newIdea);
		}
		
		// TODO:要仕様検討
		//transparent(obj.message[1]);
	}
	$("div#chat").attr("scrollTop", "1000000");
}

/**
 * WebSocketでメッセージを送信する。
 */
function send()
{
	var text = $("input#text").val();
	var parent_id = g_selected_id.length > 0 ? g_selected_id[0] : undefined;
	
	$("input#text").val("");
	socket.send({ message: [text, getGlobalColor(), parent_id] });
}

/**
 * エスケープを行う。
 * @param string msg 対象文字列
 * @return 
 */
function esc(msg)
{
	return msg.replace(/</g, '&lt;').replace(/>/g, '&gt;');
}

/**
 * 送信ボタンのクリックイベントハンドラー
 */
$("input#send_msg").click(function ()
{
	// 空文字は送信しない
	if ($("input#text").val()) {
		send();
	}
	// テキストボックスにフォーカスする
	$("input#text").focus();
});

/**
 * テキストボックスのキーダウンハンドラー
 */
$("input#text").keydown(function(e)
{
	if (e.keyCode == 13)
	{
		$("input#send_msg").click();
	}
});

/**
 * アイデアのクリックハンドラー
 */
$("div.idea").live("click", function()
{
	// クリックされたアイデアのIDを取得
	var thisId = $(this).attr("id");
	
	// 現在選択されているアイデアのIDを取得
	var prevId = g_selected_id.pop();
	
	if (prevId)
	{
		// 選択解除
		g_idea_map[String(prevId)].deselect();
	}
	
	// 異なるアイデアをクリックした場合のみ
	if (prevId != thisId)
	{
		// 選択
		g_idea_map[String(thisId)].select();
		g_selected_id.push(thisId);
	}
	$("input#text").focus();
});

/**
 * アイデアのホバーイベントハンドラー
 */
$("div.idea:parent").live("hover", function(e)
{
	// マウスオーバー時に最前面にする
	switch (e.type) {
		case "mouseenter":
			$(this).css("color", "black");
			$(this).css("z-index", 10000);
			$(this).parent().css("z-index", 10000);
			break;
		case "mouseleave":
			$(this).css("color", "white");
			$(this).css("z-index", 0);
			$(this).parent().css("z-index", 0);
			break;
		default:
			break;
	}
});

/**
 * 動的要素に対するdraggable拡張
 * @param {Object} options
 */
jQuery.fn.liveDraggable = function (options)
{
	this.live("mouseover", function()
	{
		// マウスオーバーしたアイデアのIDを取得
		var thisId = $(this).attr("id");
		
		// アイデアデータを検索
		var ideaObj = g_idea_map[String(thisId)];
		
		// 親を持っていない場合のみ、draggableに設定する
		if (ideaObj.getParentId() == "" && !$(this).data("init"))
		{
			$(this).data("init", true).parent().draggable(options)
		}
	});
};

/**
 * アイデアへのDraggable設定。
 */
$("div.idea:parent").liveDraggable({
	snap: ".idea",
	containment: "body",
	opacity: 0.5,
	cursor: 'move',
	zIndex: 10000,
	stop: function(e, ui){
		var ideaObj = g_idea_map[String($(this).children(".idea").attr("id"))];
		ideaObj.top = e.pageY;
		ideaObj.left = e.pageX;
		refreshDraw(ideaObj);
	}
});


//
// メイン処理
//

// テキストボックスにフォーカスする
$("input#text").focus();

// サーバに接続する
var socket = new io.Socket(null, {port: 80, rememberTransport: false});
socket.connect();
	
// サーバーからのメッセージ処理ハンドラを定義
socket.on('message', function(obj)
{
	if ('buffer' in obj)
	{
		$('div#chat').text("");
	  
		for (var i in obj.buffer)
		{
			message(obj.buffer[i]);
		}
	}
	else
	{
		message(obj);
	}
});

}) // end of $(function())

